package be.darling.scrt.utilities;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.ArrayList;

public class XMLWriter {
    PrintWriter out;
    ArrayList openElement = new ArrayList();
    private boolean tagOpen = false;
    StringWriter back;
    String encoding ;

    // todo call SAX events?
    // todo transform with XSLT?

    public XMLWriter() {
        this((OutputStream) null);
    }

    public XMLWriter(File file) throws FileNotFoundException {
        this(new FileOutputStream(file),"UTF-8");
    }

    public XMLWriter(OutputStream out) {
        this(out,"UTF-8");
    }

    public XMLWriter(OutputStream out, String encoding) {
        this.encoding = encoding;
        if (out == null) {
            back = new StringWriter();
            this.out = new PrintWriter(back);
        } else {
            this.out = new PrintWriter(out);
        }
        writeHeading();
    }

    public String getResult(){
        if (back!=null){
            return back.toString();
        }
        return null;
    }

    protected synchronized void writeHeading(){
        out.println("<?xml version=\"1.0\" encoding=\""+encoding+"\"?>"); // encoding?
    }

    public synchronized XMLWriter element(String name){
        // check that name is only single word
        name=noSpaces(name);
        closeTag();
        out.print("<");
        out.print(name);
        addOpenTag(name);
        tagOpen = true;
        return this;
    }

    private String noSpaces(String name) {
        return name.replaceAll(" ","_");
    }

    public synchronized XMLWriter element(String name,String data){
        return element(name).write(data).closeElement();
    }

    public synchronized XMLWriter attribute(String name,String value){
        if (tagOpen){
            out.print(" ");
            out.print(name);
            out.print("=\"");
            out.print(value);
            out.print("\" ");
        }
        return this;
    }

    public synchronized XMLWriter closeElement(){ // String name ??
        closeTag();
        if (openElement.size() > 0){
            String tagName = (String)openElement.get(openElement.size()-1);
            openElement.remove(openElement.size()-1);
            if (tagOpen){
                out.println("/>");
            } else {
                out.print("</");
                out.print(tagName);
                out.println(">");
            }
        }
        return this;
    }

    public XMLWriter write(String text){
        closeTag();
        out.print(xmlEncode(text)); // xmlencode
        return this;
    }

    public XMLWriter writeRaw(String text){
        closeTag();
        out.print(text);
        return this;
    }

    private synchronized void closeTag(){
        if (tagOpen){
            out.print(">");
            tagOpen = false;
        }
    }

    private void addOpenTag(String name) {
        openElement.add(name);
    }

    public void close() {
        // close all open tags
        closeTag();
        while(openElement.size() > 0){
            closeElement();
        }
        out.flush();
        out.close();
    }


    public static final String xmlEncode(String s){
        if (s == null) return "";
	   StringBuffer sb = new StringBuffer();
	   int n = s.length();
	   for (int i = 0; i < n; i++) {
	      char c = s.charAt(i);
	      switch (c) {
             case '\'': sb.append("&apos;"); break;
             case '\"': sb.append("&quot;"); break;
	         case '<': sb.append("&lt;"); break;
	         case '>': sb.append("&gt;"); break;
	         case '&': sb.append("&amp;"); break;


	         default:  sb.append(c); break;
	      }
	   }
	   return sb.toString();
	}

    public String XSLT(String stylesheet){
        return XSLT(new StreamSource(stylesheet));
    }

    public String XSLT(File stylesheet){
        return XSLT(new StreamSource(stylesheet));
    }
    

    public String XSLT(StreamSource stylesheet){
        // nu kan het enkel op het einde. Moet ook kunnen vanaf begin, met echte streamsource en streamresult
        StringWriter result = new StringWriter();
        try{
            Source xmlSource = new StreamSource(new java.io.StringReader(getResult())); // kan beter ;-)
            try{
                TransformerFactory tFactory = TransformerFactory.newInstance();
                Transformer trans = tFactory.newTransformer(stylesheet);
                trans.transform(xmlSource, new StreamResult(result));
            } catch (TransformerException e){
                System.out.println("XSLT error "+e+" "+e.getCause()+" "+e.getMessageAndLocation());
                e.printStackTrace();
            }
        } catch (Exception e){
            System.out.println("XSLT error "+e);
            e.printStackTrace();
        }
        return result.toString();
    }


    public static void main(String[] args) {
        File file = new File("/tmp/test.xml");
        try {
            XMLWriter w = new XMLWriter(file);
            w.element("test");
            w.element("value","value 1");
            w.element("value","value € ù utf test $<> é @ ");
            w.close();
        } catch (FileNotFoundException e) {
            System.out.println("Error "+e);
        }

    }
}

